package com.ehome.emergency.dao.impl;

import com.ehome.emergency.common.TimeConstant;
import com.ehome.emergency.dao.GenericDao;
import com.ehome.emergency.utils.Pager;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.annotations.Cache;
import org.hibernate.criterion.CriteriaSpecification;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;
import org.hibernate.transform.AliasToBeanResultTransformer;
import org.springframework.orm.hibernate5.HibernateTemplate;
import org.springframework.stereotype.Repository;

import javax.annotation.Resource;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

/**
 * BasicFramework
 *
 * @author: haoxiaolei
 * @date: 2016-02-15 11:23
 * @desc: 公共DAO实现类
 */
@Repository("baseDao")
public class GenericDaoImpl<T, PK extends Serializable> implements GenericDao<T, PK> {
    @Resource
    protected SessionFactory sessionFactory;
    @Resource
    protected HibernateTemplate hibernateTemplate;

    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    protected void clear() {
        getSession().clear();
    }

    /**
     * 保存
     *
     * @param entity 对象实体
     *
     * @return 保存后的对象
     */
    public T save(T entity) throws Exception {
        getSession().saveOrUpdate(entity);
        return entity;
    }

    /**
     * 批量保存
     *
     * @param entities 对象集合
     *
     * @return 保存后的对象集合
     */
    public Collection<T> save(Collection<T> entities) throws Exception {
        for (T entity : entities) {
            getSession().saveOrUpdate(entity);
        }
        return entities;
    }

    /**
     * 更新
     *
     * @param entity 对象实体
     *
     * @return 更新后的对象
     */
    public T update(T entity) throws Exception {
        getSession().update(entity);
        return entity;
    }

    /**
     * 批量更新
     *
     * @param entities 对象集合
     *
     * @return 更新后的对象集合
     */
    public Collection<T> update(Collection<T> entities) throws Exception {
        if (entities != null && entities.size() > 0) {
            for (T entity : entities) {
                getSession().update(entity);
            }
        }
        return entities;
    }

    /**
     * 删除
     *
     * @param entity 要删除的对象
     *
     * @return 返回删除的对象
     */
    public T delete(T entity) throws Exception {
        getSession().delete(entity);
        return entity;
    }

    /**
     * 根据Id删除对象
     *
     * @param entityClass 要删除的对象的类型
     * @param pk          对象主键
     *
     * @return 删除的对象
     */
    public T delete(final Class<T> entityClass, PK pk) throws Exception {
        T obj = findById(entityClass, pk);
        return delete(obj);
    }

    /**
     * 批量删除
     *
     * @param entities 对象集合
     *
     * @return 删除的对象集合
     */
    public List<T> delete(List<T> entities) throws Exception {
        List<T> deletedList = new ArrayList<>();
        if (entities != null && entities.size() > 0) {
            for (T entity : entities) {
                deletedList.add(delete(entity));
            }
        }
        return deletedList;
    }

    /**
     * 根据条件删除对象集合
     *
     * @param entityClass      要删除的对象的类型
     * @param detachedCriteria 删除条件，如果为null，则删除全部
     *
     * @return 删除的对象集合
     */
    public List<T> delete(final Class<T> entityClass, DetachedCriteria detachedCriteria) throws Exception {
        List<T> deletedList = new ArrayList<>();
        Criteria criteria = getCriteria(entityClass, detachedCriteria, null);
        List list = criteria.list();
        for (Object obj : list) {
            deletedList.add(delete((T) obj));
        }
        return deletedList;
    }

    /**
     * 根据某个字段查询
     * (单表操作)
     *
     * @param entityClass 要查询的对象的类型
     * @param name        字段名称
     * @param value       字段值
     */
    public List<T> find(final Class<T> entityClass, String name, String value) throws Exception {
        String sql = "from " + entityClass.getSimpleName() + " where " + name + "=:value";
        Query query = getSession().createQuery(sql);
        query.setParameter("value", value);
        // 判断是否加缓存
        Cache[] cacheAnnotation = entityClass.getAnnotationsByType(Cache.class);
        if (cacheAnnotation.length > 0) {
            query.setCacheable(true);
        }
        return query.list();
    }

    /**
     * 根据条件查询对象集合
     *
     * @param entityClass      要查询的对象的类型
     * @param detachedCriteria 查询条件，如果为null，则查询全部
     *
     * @return 对象集合
     */
    public List<T> findList(final Class<T> entityClass, DetachedCriteria detachedCriteria) throws Exception {
        return findList(entityClass, detachedCriteria, null);
    }

    /**
     * 根据条件查询对象集合,并返回指定属性(字段)
     *
     * @param entityClass      要查询的对象的类型
     * @param detachedCriteria 查询条件，如果为null，则查询全部
     * @param propertyList 查询指定的属性(字段)
     * @return 对象集合
     */
    public List<T> findList(final Class<T> entityClass, DetachedCriteria detachedCriteria, List<String> propertyList) throws Exception {
        Criteria criteria = getCriteria(entityClass, detachedCriteria, propertyList);
        return criteria.list();
    }

    /**
     * 获取前多少条数据
     *
     * @param entityClass 对象的class
     * @param _criteria    条件，如果为null，则查询全部
     * @param limit       前多少条
     */
    public List<T> findListLimitRows(Class<T> entityClass, DetachedCriteria _criteria, Integer limit) throws Exception {
        Criteria criteria = getCriteria(entityClass, _criteria, null);
        // 查询
        criteria.setFirstResult(0);
        criteria.setMaxResults(limit);
        return criteria.list();
    }

    /**
     * 分组查询
     *
     * @param entityClass      要查询的对象的类型
     * @param detachedCriteria 查询条件，如果为null，则查询全部
     * @param propertyList     group by 字段
     *
     * @return 对象集合
     */
    public List<T> findListGroupBy(final Class<T> entityClass, DetachedCriteria detachedCriteria, List<String> propertyList) throws Exception {
        Criteria criteria = getCriteria(entityClass, detachedCriteria, propertyList);
        ProjectionList projectionList = Projections.projectionList();
        for (String p : propertyList) {
            projectionList.add(Projections.groupProperty(p));
        }
        criteria.setProjection(projectionList);
        return criteria.list();
    }

    /**
     * 根据条件分页查询对象集合
     *
     * @param entityClass      要查询的对象的类型
     * @param detachedCriteria 查询条件，如果为null，则查询全部
     * @param pager            分页对象
     *
     * @return 对象集合
     */
    public List<T> findListPager(final Class<T> entityClass, DetachedCriteria detachedCriteria, Pager pager) throws Exception {
        return findListPager(entityClass, detachedCriteria, pager, null);
    }

    /**
     * 根据条件分页查询对象集合,并返回指定属性(字段)
     *
     * @param entityClass      要查询的对象的类型
     * @param detachedCriteria 查询条件，如果为null，则查询全部
     * @param pager            分页对象
     * @param propertyList     查询指定的属性(字段)
     *
     * @return 对象集合
     */
    public List<T> findListPager(final Class<T> entityClass, DetachedCriteria detachedCriteria, Pager pager, List<String> propertyList) throws Exception {
        Criteria criteria = getCriteria(entityClass, detachedCriteria, propertyList);
        // 统计总记录数
        criteria.setProjection(Projections.rowCount());
        pager.setTotalRecords(Integer.valueOf(criteria.uniqueResult().toString()));
        criteria.setProjection(null);       //清空projection，以便取得记录
        criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);//设置查询结果为实体对象

        // 查询
        criteria.setFirstResult((pager.getPage() - 1) * pager.getRows());
        criteria.setMaxResults(pager.getRows());

        // 排序(仅单列排序)
        if (StringUtils.isNotBlank(pager.getOrderby()) && pager.getOrderby().split(" ").length == 2) {
            String[] orderBy = pager.getOrderby().split(" ");
            pager.setOrderby(orderBy[1]);
            pager.setOrderbyProperty(orderBy[0]);
        }
        if (StringUtils.isNotEmpty(pager.getOrderby()) && Pager.Orderby.ASC.name().equalsIgnoreCase(pager.getOrderby()) && StringUtils.isNotBlank(pager.getOrderbyProperty())) {
            String[] props = pager.getOrderbyProperty().split(",");
            for (String prop : props) {
                criteria.addOrder(Order.asc(prop));
            }
        } else if (StringUtils.isNotBlank(pager.getOrderby()) && Pager.Orderby.DESC.name().equalsIgnoreCase(pager.getOrderby()) && StringUtils.isNotBlank(pager.getOrderbyProperty())) {
            String[] props = pager.getOrderbyProperty().split(",");
            for (String prop : props) {
                criteria.addOrder(Order.desc(prop));
            }
        }
        return criteria.list();
    }

    /**
     * 根据条件查询条件内的所有对象集合
     *
     * @param entityClass      要查询的对象的类型
     *
     * @return 对象集合
     */
    public List<T> findAll(final Class<T> entityClass) throws Exception {
        return findList(entityClass, null, null);
    }

    /**
     * 根据主键查询对象
     *
     * @param entityClass 对象的class
     * @param pk          主键
     *
     * @return 对象实体
     */
    public T findById(final Class<T> entityClass, PK pk) throws Exception {
        return (T) getSession().get(entityClass, pk);
    }

    /**
     * 根据主键批量查询对象
     *
     * @param entityClass 对象的class
     * @param ids         主键集合
     *
     * @return 对象实体
     */
    @SafeVarargs
    public final List<T> findByIds(final Class<T> entityClass, PK... ids) throws Exception {
        List<T> entityList = new ArrayList<>();
        for (int i = 0; i < ids.length; i++) {
            T t = hibernateTemplate.get(entityClass, ids[i]);
            entityList.add(t);
        }
        return entityList;
    }

    /**
     * 根据HQL语句查询
     * @param hql    查询语句,支持连接查询和多条件查询
     * @param params 参数数组,代替hql中的"?"号
     * @return 结果集List
     */
    public List<?> findByHql(String hql, Object... params) {
        return hibernateTemplate.find(hql, params);
    }

    /**
     * 根据HQL语句查询
     * @param hql         查询语句,支持连接查询和多条件查询 例如：from User u
     * @param pager       分页对象
     * @param queryColumn 要查询的字段字符串 例如：u.id, u.name, u.age
     * @param params      参数数组,代替hql中的"?"号
     * @return 结果集List
     */
    public List<?> findPagerByHql(String hql, Pager pager, String queryColumn, Object... params) throws Exception {
        Query query = getSession().createQuery(queryColumn + " " + hql);
        for (int i = 0; i < params.length; i++) {
            if (params[i] instanceof String) {
                query.setString(i, params[i].toString());
            } else if (params[i] instanceof Long) {
                query.setLong(i, Long.valueOf(params[i].toString()));
            } else if (params[i] instanceof Integer) {
                query.setInteger(i, Integer.valueOf(params[i].toString()));
            } else if (params[i] instanceof Date) {
                query.setDate(i, DateUtils.parseDate(params[i].toString(), TimeConstant.Y_M_D_HMS));
            } else if (params[i] instanceof Double) {
                query.setDouble(i, Double.valueOf(params[i].toString()));
            } else if (params[i] instanceof Float) {
                query.setFloat(i, Float.valueOf(params[i].toString()));
            } else if (params[i] instanceof Boolean) {
                query.setBoolean(i, Boolean.valueOf(params[i].toString()));
            }
        }
        // 统计总记录数
        String cntHql = "select count(1) " + hql;
        Query cntQuery = getSession().createQuery(cntHql);
        pager.setTotalRecords(Integer.valueOf(cntQuery.uniqueResult().toString()));

        query.setFirstResult((pager.getPage() - 1) * pager.getRows());
        query.setMaxResults(pager.getRows());
        return query.list();
    }

    /**
     * 根据SQL语句查询
     * @param sql    查询语句,支持连接查询和多条件查询
     * @param params 参数数组,代替hql中的"?"号
     * @return 结果集List
     */
    public List<?> findBySQL(String sql, Object... params) throws Exception {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        for (int i = 0; i < params.length; i++) {
            if (params[i] instanceof String) {
                query.setString(i, params[i].toString());
            } else if (params[i] instanceof Long) {
                query.setLong(i, Long.valueOf(params[i].toString()));
            } else if (params[i] instanceof Integer) {
                query.setInteger(i, Integer.valueOf(params[i].toString()));
            } else if (params[i] instanceof Date) {
                query.setDate(i, DateUtils.parseDate(params[i].toString(), TimeConstant.Y_M_D_HMS));
            } else if (params[i] instanceof Double) {
                query.setDouble(i, Double.valueOf(params[i].toString()));
            } else if (params[i] instanceof Float) {
                query.setFloat(i, Float.valueOf(params[i].toString()));
            } else if (params[i] instanceof Boolean) {
                query.setBoolean(i, Boolean.valueOf(params[i].toString()));
            }
        }
        return query.list();
    }

    /**
     * 执行原生SQL
     * @param sql
     */
    public int excuteBySql(String sql) throws Exception {
        SQLQuery query = this.getSession().createSQLQuery(sql);
        return query.executeUpdate();
    }

    /**
     * 根据某个字段查询唯一一条数据 (单表操作)
     *
     * @param entytyClz 要删除的对象的类型
     * @param name      字段名称
     * @param value     字段值
     */
    public T findUnique(final Class<T> entytyClz, String name, Object value) throws Exception {
        String sql = "from " + entytyClz.getSimpleName() + " where " + name + "=:value";
        Query query = getSession().createQuery(sql);
        // 判断是否加缓存
        Cache[] cacheAnnotation = entytyClz.getAnnotationsByType(Cache.class);
        if (cacheAnnotation.length > 0) {
            query.setCacheable(true);
        }
        query.setParameter("value", value);
        return (T) query.uniqueResult();
    }

    /**
     * 统计总记录
     *
     * @param entityClass      对象的class
     * @param detachedCriteria 条件，如果为null，则查询全部
     *
     * @return 总记录数量
     */
    public Long count(final Class<T> entityClass, DetachedCriteria detachedCriteria) throws Exception {
        Criteria criteria = getCriteria(entityClass, detachedCriteria, null);
        criteria.setProjection(Projections.rowCount());
        return Long.valueOf(criteria.uniqueResult().toString());
    }

    /**
     * 执行update 或 delete语句
     *
     * @param hql    update或delete语句，
     * @param params 参数数组,代替hql中的"?"号
     *
     * @return 更新或删除了多少数据
     */
    public int executeUpdate(String hql, Object... params) {
        return hibernateTemplate.bulkUpdate(hql, params);
    }


    private Criteria getCriteria(final Class<T> entityClass, DetachedCriteria detachedCriteria, List<String> propertyList) throws Exception {
        Criteria criteria;
        if (detachedCriteria != null) {
            criteria = detachedCriteria.getExecutableCriteria(getSession());
        } else {
            criteria = getSession().createCriteria(entityClass);
        }

        if (propertyList != null && propertyList.size() > 0) {
            ProjectionList projectionList = Projections.projectionList();
            for (String prop : propertyList) {
                projectionList.add(Projections.property(prop).as(prop));
            }
            criteria.setProjection(projectionList);
            criteria.setResultTransformer(new AliasToBeanResultTransformer(entityClass));
        }
        // 判断是否加缓存
        Cache[] cacheAnnotation = entityClass.getAnnotationsByType(Cache.class);
        if (cacheAnnotation.length > 0) {
            criteria.setCacheable(true);
        }
        return criteria;
    }
}
